﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

using System.Runtime.InteropServices;

using static BorderlessForm.NativeCodes.NativeMethods;
using static BorderlessForm.NativeCodes.NativeConstants;
using static BorderlessForm.NativeCodes.NativeStructures;

namespace BorderlessForm
{
    /// <summary>
    /// 为<see cref=""/>阴影背景提供支持
    /// </summary>
    internal sealed class ShadowForm : Form
    {
        #region 字段

        // 背景图片
        private readonly Bitmap _background;

        #endregion 字段

        #region 构造
        public IntPtr pHwd = IntPtr.Zero;
        public ShadowForm(Color backColor, Size size)
            : this(BitmapUtility.CreateTranslucentBitmap(127, backColor, size)) { }

        public ShadowForm(Bitmap bg)
        {
            if (bg == null)
                throw new ArgumentNullException(nameof(bg));
            Size = bg.Size;
            _background = bg;
            ShowInTaskbar = false;
        }

        #endregion 构造

        #region 方法

        private void InitializeStyles()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            UpdateStyles();
        }

        public void UpdateBmp(Bitmap bmp)
        {
            if (!IsHandleCreated) return;

            if (!Image.IsCanonicalPixelFormat(bmp.PixelFormat) || !Image.IsAlphaPixelFormat(bmp.PixelFormat))
                throw new ArgumentException(@"位图格式不正确", nameof(bmp));

            IntPtr oldBits = IntPtr.Zero;
            IntPtr screenDc = GetDC(IntPtr.Zero);
            IntPtr hBmp = IntPtr.Zero;
            IntPtr memDc = CreateCompatibleDC(screenDc);

            try
            {
                POINT formLocation = new POINT(Left, Top);
                SIZE bitmapSize = new SIZE(bmp.Width, bmp.Height);
                BLENDFUNCTION blendFunc = new BLENDFUNCTION(
                    AC_SRC_OVER,
                    0,
                    255,
                    AC_SRC_ALPHA);

                POINT srcLoc = new POINT(0, 0);

                hBmp = bmp.GetHbitmap(Color.FromArgb(0));
                oldBits = SelectObject(memDc, hBmp);

                UpdateLayeredWindow(
                    Handle,
                    screenDc,
                    ref formLocation,
                    ref bitmapSize,
                    memDc,
                    ref srcLoc,
                    0,
                    ref blendFunc,
                    ULW_ALPHA);
            }
            finally
            {
                if (hBmp != IntPtr.Zero)
                {
                    SelectObject(memDc, oldBits);
                    DeleteObject(hBmp);
                }

                ReleaseDC(IntPtr.Zero, screenDc);
                DeleteDC(memDc);
            }
        }

        #endregion 方法

        #region 重写

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cParms = base.CreateParams;
                cParms.ExStyle |= (int)WS_EX_LAYERED | (int)WS_EX_NOACTIVATE;
                return DesignMode ? base.CreateParams : cParms;
            }
        }

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_NCHITTEST:
                    {
                        Point pt = PointToClient(new Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16 & 0xFFFF));

                        if (pt.Y < 0)
                        {
                            m.Result = (IntPtr)HTCAPTION;
                        }

                        Point ptScreen = PointToScreen(pt);
                        const int borderRegionWidth = 2 + 2;
                        bool bTop = ptScreen.Y >= Top && ptScreen.Y <= Top + borderRegionWidth + 4;
                        bool bBottom = ptScreen.Y <= Bottom && ptScreen.Y >= Bottom - borderRegionWidth;
                        bool bLeft = ptScreen.X >= Left && ptScreen.X <= Left + borderRegionWidth + 2;
                        bool bRight = ptScreen.X <= Right + 18 && ptScreen.X >= Right - 18;

                        if (bLeft)
                        {
                            if (bTop)
                                m.Result = (IntPtr)HTTOPLEFT;
                            else if (bBottom)
                                m.Result = (IntPtr)HTBOTTOMLEFT;
                            else
                                m.Result = (IntPtr)HTLEFT;
                        }
                        else if (bRight)
                        {
                            if (bTop)
                                m.Result = (IntPtr)HTTOPRIGHT;
                            else if (bBottom)
                                m.Result = (IntPtr)HTBOTTOMRIGHT;
                            else
                                m.Result = (IntPtr)HTRIGHT;
                        }
                        else if (bTop)
                        {
                            m.Result = (IntPtr)HTTOP;
                        }
                        else if (bBottom)
                        {
                            m.Result = (IntPtr)HTBOTTOM;
                        }
                        SendMessage(pHwd, WM_NCHITTEST, (int)m.WParam, (int)m.LParam);
                    }
                    break;
                case WM_NCLBUTTONDOWN:
                    {
                        SetForegroundWindow(pHwd);
                        //SendMessage(pHwd, WM_NCACTIVATE, (int)1, (int)m.LParam);
                        SendMessage(pHwd, WM_NCLBUTTONDOWN, (int)m.WParam, (int)m.LParam);
                    }
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            InitializeStyles();
            base.OnHandleCreated(e);
        }

        protected override void OnHandleDestroyed(EventArgs e)
        {
            _background.Dispose();
            base.OnHandleDestroyed(e);
        }

        protected override void OnLoad(EventArgs e)
        {
            UpdateBmp(_background);
            base.OnLoad(e);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            e.Cancel = true;
            base.OnClosing(e);
        }

        protected override void OnClientSizeChanged(EventArgs e)
        {
            if (FormBorderStyle != FormBorderStyle.None)
                FormBorderStyle = FormBorderStyle.None;

            base.OnClientSizeChanged(e);
        }

        #endregion 重写

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // XFormShadow
            // 
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Name = "XFormShadow";
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.XFormShadow_KeyDown);
            this.ResumeLayout(false);

        }

        private void XFormShadow_KeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine(DateTime.Now);
        }
    }
}
